{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Create a QComponent - Advanced"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit_metal import draw, Dict\n",
    "from qiskit_metal.toolbox_metal import math_and_overrides\n",
    "from qiskit_metal.qlibrary.core import QComponent\n",
    "import qiskit_metal as metal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "design = metal.designs.DesignPlanar()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qubits and Junctions\n",
    "\n",
    "The vast majority of junction management is actually under the QRenderers. The only information that a component designer needs to provide, is a linestring and width which indicates the location and orientation of a given junction. We can see this from a couple extracted lines of code from `TransmonPocket`\n",
    "\n",
    "`...`\n",
    "\n",
    "`rect_jj = draw.LineString([(0, -pad_gap / 2), (0, +pad_gap / 2)])`\n",
    "\n",
    "`...`\n",
    "\n",
    "`self.add_qgeometry('junction', dict(rect_jj=rect_jj), width=p.inductor_width)`\n",
    "\n",
    "\n",
    "In this case, the linestring is drawn between the two charge islands of the `TransmonPocket`. Much more of the junctions options are from renderer options added when the QRenderers are initiated. These are covered more in the renderer tutorials and sessions.\n",
    "\n",
    "It should be noted, currently multiple junctions in a component will receive the same renderer options. This is fine if, say, making a symmetric SQUID, though if trying to have asymmetry, (or, say fluxonium), a manner to handled multiple junction renderer options in a component is required."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit_metal.qlibrary.qubits.transmon_pocket import TransmonPocket\n",
    "?TransmonPocket"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exteriors, Interiors, and MultiPolygons\n",
    "\n",
    "As was shown in 3.1, there is a great amount of flexibility already present in Metal for what a component can be, though as it is still in development, there are some limitations with respect to if renderers can accurately render a given shape, say, a multi-faceted polygon where some facets are composed of splines. What capabilities are currently missing and would be beneficial to be added are all part of the development process.\n",
    "\n",
    "Currently, a poly can be generated with interior cut outs, such as the smiley face previously,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "face = draw.shapely.geometry.Point(0, 0).buffer(1)\n",
    "eye = draw.shapely.geometry.Point(0, 0).buffer(0.2)\n",
    "eye_l = draw.translate(eye, -0.4, 0.4)\n",
    "eye_r = draw.translate(eye, 0.4, 0.4)\n",
    "\n",
    "smile = draw.shapely.geometry.Point(0, 0).buffer(0.8)\n",
    "cut_sq = draw.shapely.geometry.box(-1, -0.3, 1, 1)\n",
    "smile = draw.subtract(smile, cut_sq)\n",
    "face = draw.subtract(face, smile)\n",
    "face = draw.subtract(face, eye_r)\n",
    "face = draw.subtract(face, eye_l)\n",
    "face"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This differs from qgeometries which have `subtract=True`, as that specifically sets that geometry to be \"etched\" from the ground plane. The polygon face is composed of an exterior;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "face.exterior"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "and interiors, such as;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "face.interiors[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A renderer must recognize the difference between these shapes, as the current QRenderers do. This allows for the component designer to generate complex shapes, without having to worry about how to add the qgeometries in any particular manner. This is also true with MultiPolygons."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "big_square = draw.rectangle(10,10,0,0)\n",
    "cut_rectangle = draw.rectangle(12,1,0,0)\n",
    "multi_poly = draw.subtract(big_square, cut_rectangle)\n",
    "multi_poly"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "type(multi_poly)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The MultiPolygon can still just be passed to add_qgeometry as one would with a regular polygon. It is broken up behind the scenes so two separate rectangles (with the appropriate coordinates) are added to the poly qgeometry table. This is handled by the add_qgeometry method of QGeometryTables."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "?metal.qgeometries.QGeometryTables.add_qgeometry"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This method also handles rounding of coordinates to try and avoid any numerical errors. It is called by `metal.qlibrary.core.QComponent.add_qgeometry` and should not be called directly."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## QComponent Inheritance\n",
    "\n",
    "As is the case with python classes, one can extend a given component by creating a qcomponent which inherits said class, making it a parent/child relationship. While python does support multiple inheritances, Metal may run into some bugs, so it is best to keep inheritances as single paths of heritage.\n",
    "\n",
    "A good example is `TransmonPocketCL`, which adds a \"charge line\" the a \"standard\" `TransmonPocket`. As can be seen in the below code, none of the charge islands or other connection pads are present, but will still be generated via the `super().make()` line in the `make()` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from qiskit_metal import draw, Dict\n",
    "from qiskit_metal.qlibrary.qubits.transmon_pocket import TransmonPocket\n",
    "\n",
    "\n",
    "class TransmonPocketCL(TransmonPocket):  # pylint: disable=invalid-name\n",
    "    \"\"\"\n",
    "    The base `TransmonPocketCL` class\n",
    "\n",
    "    Inherits `TransmonPocket` class\n",
    "\n",
    "    Description:\n",
    "        Create a standard pocket transmon qubit for a ground plane,\n",
    "        with two pads connected by a junction (see drawing below).\n",
    "\n",
    "        Connector lines can be added using the `connection_pads`\n",
    "        dictionary. Each connector line has a name and a list of default\n",
    "        properties.\n",
    "\n",
    "        This is a child of TransmonPocket, see TransmonPocket for the variables and\n",
    "        description of that class.\n",
    "\n",
    "    ::\n",
    "\n",
    "        _________________\n",
    "        |               |\n",
    "        |_______________|       ^\n",
    "        ________x________       |  N\n",
    "        |               |       |\n",
    "        |_______________|\n",
    "\n",
    "\n",
    "    .. image::\n",
    "        Component_Qubit_Transmon_Pocket_CL.png\n",
    "\n",
    "\n",
    "    Charge Line:\n",
    "        * make_CL (bool): If a chargeline should be included.\n",
    "        * cl_gap (string): The cpw dielectric gap of the charge line.\n",
    "        * cl_width (string): The cpw width of the charge line.\n",
    "        * cl_length (string):  The length of the charge line 'arm' coupling the the qubit pocket.\n",
    "          Measured from the base of the 90 degree bend.\n",
    "        * cl_ground_gap (string):  How much ground is present between the charge line and the\n",
    "          qubit pocket.\n",
    "        * cl_pocket_edge (string): What side of the pocket the charge line is.\n",
    "          -180 to +180 from the 'west edge', will round to the nearest 90.\n",
    "        * cl_off_center (string):  Distance from the center axis the qubit pocket is referenced to\n",
    "    \"\"\"\n",
    "    component_metadata = Dict(short_name='Q', _qgeometry_table_poly='True')\n",
    "    \"\"\"Component metadata\"\"\"\n",
    "\n",
    "    default_options = Dict(\n",
    "        make_CL=True,\n",
    "        cl_gap='6um',  # the cpw dielectric gap of the charge line\n",
    "        cl_width='10um',  # the cpw trace width of the charge line\n",
    "        # the length of the charge line 'arm' coupling the the qubit pocket.\n",
    "        cl_length='20um',\n",
    "        # Measured from the base of the 90 degree bend\n",
    "        cl_ground_gap=\n",
    "        '6um',  # how much ground between the charge line and the qubit pocket\n",
    "        # -180 to +180 from the 'left edge', will round to the nearest 90.\n",
    "        cl_pocket_edge='0',\n",
    "        cl_off_center=\n",
    "        '100um',  # distance from the center axis the qubit pocket is built on\n",
    "    )\n",
    "    \"\"\"Default drawing options\"\"\"\n",
    "\n",
    "    def make(self):\n",
    "        \"\"\"Define the way the options are turned into QGeometry.\"\"\"\n",
    "        super().make()\n",
    "\n",
    "        if self.options.make_CL == True:\n",
    "            self.make_charge_line()\n",
    "\n",
    "\n",
    "#####################################################################\n",
    "\n",
    "    def make_charge_line(self):\n",
    "        \"\"\"Creates the charge line if the user has charge line option to TRUE\n",
    "        \"\"\"\n",
    "\n",
    "        # Grab option values\n",
    "        name = 'Charge_Line'\n",
    "\n",
    "        p = self.p\n",
    "\n",
    "        cl_arm = draw.box(0, 0, -p.cl_width, p.cl_length)\n",
    "        cl_cpw = draw.box(0, 0, -8 * p.cl_width, p.cl_width)\n",
    "        cl_metal = draw.unary_union([cl_arm, cl_cpw])\n",
    "\n",
    "        cl_etcher = draw.buffer(cl_metal, p.cl_gap)\n",
    "\n",
    "        port_line = draw.LineString([(-8 * p.cl_width, 0),\n",
    "                                     (-8 * p.cl_width, p.cl_width)])\n",
    "\n",
    "        polys = [cl_metal, cl_etcher, port_line]\n",
    "\n",
    "        # Move the charge line to the side user requested\n",
    "        cl_rotate = 0\n",
    "        if (abs(p.cl_pocket_edge) > 135) or (abs(p.cl_pocket_edge) < 45):\n",
    "            polys = draw.translate(\n",
    "                polys, -(p.pocket_width / 2 + p.cl_ground_gap + p.cl_gap),\n",
    "                -(p.pad_gap + p.pad_height) / 2)\n",
    "            if (abs(p.cl_pocket_edge) > 135):\n",
    "                p.cl_rotate = 180\n",
    "        else:\n",
    "            polys = draw.translate(\n",
    "                polys, -(p.pocket_height / 2 + p.cl_groundGap + p.cl_gap),\n",
    "                -(p.pad_width) / 2)\n",
    "            cl_rotate = 90\n",
    "            if (p.cl_pocket_edge < 0):\n",
    "                cl_rotate = -90\n",
    "\n",
    "        # Rotate it to the pockets orientation\n",
    "        polys = draw.rotate(polys, p.orientation + cl_rotate, origin=(0, 0))\n",
    "\n",
    "        # Move to the final position\n",
    "        polys = draw.translate(polys, p.pos_x, p.pos_y)\n",
    "\n",
    "        [cl_metal, cl_etcher, port_line] = polys\n",
    "\n",
    "        # Generating pins\n",
    "        points = list(draw.shapely.geometry.shape(port_line).coords)\n",
    "        self.add_pin(name, points, p.cl_width)  # TODO: chip\n",
    "\n",
    "        # Adding to element table\n",
    "        self.add_qgeometry('poly', dict(cl_metal=cl_metal))\n",
    "        self.add_qgeometry('poly', dict(cl_etcher=cl_etcher), subtract=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see this is the case by generating a TransmonPocketCL in the GUI."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gui = metal.MetalGUI(design)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_transmon_cl = TransmonPocketCL(design,'my_transmon_cl',options=dict(connection_pads=dict(a=dict(),b=dict(loc_W=-1))))\n",
    "gui.rebuild()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_transmon_cl.options"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see that `my_transmon_cl` inherited the appropriate options from `TransmonPocket`, and even got the junction renderer options since its parent class does declare `_qgeometry_table_junction='True'`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gui.main_window.close()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
